#ifndef _BACKTRACKPARSER_H_
#define _BACKTRACKPARSER_H_

#include <map>
#include <iostream>

using namespace std;

#include "lexer.h"
#include "parser.h"
#include "common.h"
#include "error.h"
#include "RecognitionException.h"
#include "NoViableAltException.h"

class BacktrackParser : public Parser {
public:
    BacktrackParser(Lexer *input): Parser(input) { }

    void clear_memo() {
        list_memo.clear();
    }

    void stat() {
        if (speculate_stat_alt1()) {
            list();
            match(EOF_TYPE);
        }
        else if (speculate_stat_alt2()) {
            cout << "predict alternative 2" << endl;
            assign();
            match(EOF_TYPE);
        }
        else {
            string s = "expecting stat found ";
            s += LT(1)->text;
            throw Error(s);
        }
    }

    bool speculate_stat_alt1() {
        cout << "attemp alternative 1" << endl;
        bool success = true;
        mark();
        try {
            list();
            match(EOF_TYPE);
        }
        catch (RecognitionException e) {
            success = false;
        }
        release();
        return success;
    }

    bool speculate_stat_alt2() {
        cout << "attemp alternative 2" << endl;
        bool success = true;
        mark();
        try {
            assign();
            match(EOF_TYPE);
        }
        catch (RecognitionException e) {
            success = false;
        }
        release();
        return success;
    }

    void assign() {
        list();
        match(EQUALS);
        list();
    }

    void _list() {
        cout << "parse list rule at token index " << index() << endl;
        match(LBRACK);
        elements();
        match(RBRACK);
    }

protected:
    map<int, int> list_memo;

public:
    void list() {
        RecognitionException e("nullptr");
        bool failed = false;
        int start_token_index = index();
        if (is_speculating() && already_parsed_rule(list_memo))
            return ;
        try {
            _list();
        }
        catch (RecognitionException re) {
            failed = true;
            e = re;
        }
        if (is_speculating())
            memoize(list_memo, start_token_index, failed);
        if (failed)
            throw e;
    }

    void elements() {
        element();
        while (LA(1) == COMMA) {
            match(COMMA);
            element();
        }
    }

    void element() {
        if (LA(1) == NAME && LA(2) == EQUALS) {
            match(NAME);
            match(EQUALS);
            match(NAME);
        }
        else if (LA(1) == NAME)
            match(NAME);
        else if (LA(1) == LBRACK)
            list();
        else {
            string s = "expecting name or list; found ";
            s += LT(1)->text;
            throw NoViableAltException(s);
        }
    }
};

#endif
